﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

using alib.Enumerable;
using alib.BitArray;
using System.Reflection;
using alib.Reflection;
using alib.Array;
using alib.Collections.ReadOnly;
using alib.Memory;

using agree.configuration;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Restrictors. We were hacking in:         ORTH  LKEYS RNAME 
	/// PET settings specify:		       LTOP              RNAME  RELS  HCONS
	/// Combined, except LTOP: (r_pack)          ORTH  LKEYS RNAME  RELS  HCONS
	/// 
	/// 1. In 2013, don't restrict ORTH because you'll miss unification failures on token-mapping 'bracketing' in 
	/// the substructure of ORTH, i.e.
	///		bracket_null
	///			[#2 frag_r_mod_np_i_rule].orth.lb
	///		bracket_cons
	///			[#1 paired_bracket_rule].args.first.orth.lb
	///			
	///	2. Similar problem restricting LKEYS.
	///	
	/// 3. One of the original problems was that the packing restrictor in the PET config file contains 
	/// LTOP, which is can not be be present in a restrictor used for index accessibility filtering in 
	/// generation. I suspect this issue will be revisited for a proper solution.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class Restrictors : has_grammar_base
	{
		public unsafe Restrictors(IRtParent parent, FeatMgr ftm, String s_name, Restrictor r_none)
			: base(parent, ftm, s_name)
		{
			this.r_none = r_none;
		}

		public Restrictor r_none;
		public Restrictor r_pack;
		public Restrictor r_args;
		public Restrictor r_pack_args;

		public void SetRestrictors(IEnumerable<int> dtr_feats, IEnumerable<int> pack_feats)
		{
			var ba = new BitArr(ftm.Count, ftm.GetFeatureIndex(ftm.AgreeConfig.Grammar.RuleArgsPath[0]));
			foreach (int i_feat in dtr_feats)
				ba[i_feat] = true;
			r_args = new Restrictor(ftm, Tfs.Flags.DeleteArgs, ba);

			r_pack = pack_feats.Any() ? new Restrictor(ftm, Tfs.Flags.Restrict, pack_feats) : r_none;

			r_pack_args = new Restrictor(ftm, Tfs.Flags.DeleteArgs | Tfs.Flags.Restrict, r_pack.ba | r_args.ba);

#if AUTOTUNE
			rgrgfix_autotune = rgrgfix_by_type;
#endif
		}

		public Restrictor FromTfsFlags(Tfs.Flags flags)
#if report_restrictor_usage
		{
			String sz;
			var z = _FromTfsFlags(flags);
			if (z == r_pack_args)
				sz = "r_pack_args";
			else if (z == r_pack)
				sz = "r_pack";
			else if (z == r_args)
				sz = "r_args";
			else if (z == r_none)
				sz = "r_none";
			else
				sz = "???";

			var smi = new StackTrace(1)
				.GetFrames()
				.Select(f =>
					{
						var mi = f.GetMethod();
						return mi.DeclaringType._Name() + "." + mi.Name;
					})
				.First(mb => !mb.Contains("Unif") && !mb.Contains("__"));

			smi = String.Format("'{0}' is using restrictor '{1}' {2}", smi, sz, z.ToString());
			bool b;
			lock (smis)
				b = smis.Add(smi);
			if (b)
				Debug.WriteLine(smi);

			//Debug.Print(sz);
			return z;
		}
		HashSet<String> smis = new HashSet<String>();
		public Restrictor _FromTfsFlags(Tfs.Flags flags)
#endif
		{
			var ro = flags & (Tfs.Flags.DeleteArgs | Tfs.Flags.Restrict);

			if (ro == (Tfs.Flags.DeleteArgs | Tfs.Flags.Restrict))
				return r_pack_args;
			else if (ro == Tfs.Flags.Restrict)
				return r_pack;
			else if (ro == Tfs.Flags.DeleteArgs)
				return r_args;
			return r_none;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	///
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public unsafe sealed class Restrictor : IIdentity, schema.ρρFeatMgr
	{
		public Restrictor(FeatMgr ftm, Tfs.Flags opts, BitArr ba)
		{
			this.ftm = ftm;
			this.opts = opts;
			this.ba = ba;
			if (ba.Count != ftm.Count)
				throw new Exception();

			int[][] rgrgfix = ftm.rgrgfix_by_type;
			int c_types = rgrgfix.Length;

			var hs = new HashSet<int[]>(IntArrayEqualityComparer.Default);
			int[] rg;
			for (int i = 0; i < c_types; i++)
			{
				foreach (int fix in rg = rgrgfix[i])
					if (ba[fix])
					{
						hs.Add(rg.Where(q => !ba[q]).ToArray());
						goto ok;
					}
				hs.Add(rg);
			ok: ;
			}

			int cb_idx = c_types * sizeof(long); // ushort**; using 64-bits for uniform shift
			int cb_tot = cb_idx + (hs.Sum(_rg => _rg.Length + 2) * sizeof(ushort));

			//ushort** ppus = this.R = (ushort**)Marshal.AllocHGlobal(cb_tot);
			ushort** ppus = this.R = (ushort**)Virtual.VirtualAlloc(
				IntPtr.Zero,
				new UIntPtr((uint)cb_tot),
				Virtual.AllocationType.COMMIT | Virtual.AllocationType.RESERVE,
				Virtual.MemoryProtection.READWRITE);

			ushort* pus = (ushort*)((long*)R + c_types);

			var d = hs.ToReadOnlyDictionary(hs.Count, hrg =>
			{
				*pus++ = (ushort)hrg.Length;
				IntPtr ret = (IntPtr)pus;
				foreach (int q in hrg)
					*pus++ = (ushort)q;
				*pus++ = 0xFFFF;
				return ret;
			}, IntArrayEqualityComparer.Default);

			for (int i = 0; i < c_types; i++)
			{
				foreach (int fix in rg = rgrgfix[i])
					if (ba[fix])
					{
						rg = rg.Where(q => !ba[q]).ToArray();
						break;
					}
				*(long*)ppus = (long)(ushort*)d[rg];
				ppus = (ushort**)((long)ppus + sizeof(long));
			}

			Virtual.MemoryProtection mp_prev;
			Virtual.VirtualProtect(new IntPtr(R), new UIntPtr((uint)cb_tot), Virtual.MemoryProtection.READONLY, out mp_prev);
		}
		public Restrictor(FeatMgr ftm, Tfs.Flags opts, IEnumerable<int> feats)
			: this(ftm, opts, new BitArr(ftm.Count, feats))
		{
		}
		public Restrictor(FeatMgr ftm, Tfs.Flags opts, IEnumerable<String> feats)
			: this(ftm, opts, ftm.ConvertAll(feats))
		{
		}
		public Restrictor(FeatMgr ftm)
			: this(ftm, Tfs.Flags.None, new BitArr(ftm.Count))
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly BitArr ba;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly ushort** R;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Tfs.Flags opts;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly FeatMgr ftm;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatMgr FeatMgr { [DebuggerStepThrough] get { return ftm; } set { throw alib.not.valid; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool NoRestriction { [DebuggerStepThrough]  get { return ba.IsAllZeros; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int RestrictedCount { [DebuggerStepThrough]  get { return ba.OnesCount; } }

		public bool this[int i_feat] { [DebuggerStepThrough]  get { return ba[i_feat]; } }

		public bool this[String s_feat]
		{
			[DebuggerStepThrough] 
			get
			{
				var fi = ftm[s_feat];
				return fi != null && fi.i_feat < ba.Count && ba[fi.i_feat];
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatureInfo[] FeatureInfoList
		{
			get
			{
				if (NoRestriction)
					return FeatureInfo.None;
				var arr = new FeatureInfo[RestrictedCount];
				int i = 0;
				foreach (var i_feat in ba.OnesPositions())
					arr[i++] = ftm[i_feat];
				return arr;
			}
		}

		public override String ToString()
		{
			var s = NoRestriction ? "(no features restricted)" : FeatureInfoList.Select(fi => fi.FEATURE).StringJoin(" ");
			return opts.ToString() + " " + s;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IIdentity Trace { get { return ftm; } }
	};
}